package com.xq.sqlSession;

import com.xq.pojo.BoundSql;
import com.xq.pojo.Configuration;
import com.xq.pojo.MappedStatement;
import com.xq.util.GenericTokenParser;
import com.xq.util.ParameterMapping;
import com.xq.util.ParameterMappingTokenHandler;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: xiaoqiang
 * @Description:
 * @Date: 2021/5/31
 * @Version: 1.0
 */
public class SimpleExecutor implements Execotor {

    /**
     * 执行sql
     *
     * @param configuration
     * @param param
     * @return
     */
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... param) throws SQLException, NoSuchFieldException, IllegalAccessException, ClassNotFoundException, InstantiationException, IntrospectionException, InvocationTargetException {

        // 1 获取数据库驱动
        Connection connection = configuration.getDataSource().getConnection();
        /**2 获取sql 获取到的sql是 select * from tablename where id=#{id} and name=#{name}
         所以需要转换为 select * from tablename where id=？ and name=？
         */
        String sql = mappedStatement.getSql();
        BoundSql boundSql = parseSql(sql);

        //3 获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getExecuteSql());
        //4 设置参数
        //获取sql入参的类型全路径
        String paramterType = mappedStatement.getParamterType();
        if (paramterType != null) {
            Class<?> aClass = null;
            try {
                aClass = Class.forName(paramterType);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            List<ParameterMapping> params = boundSql.getParam();
            for (int i = 0; i < params.size(); i++) {
                ParameterMapping parameterMapping = params.get(i);
                String content = parameterMapping.getContent();
                Field declaredField = aClass.getDeclaredField(content);
                //暴力破解，防止private属性
                declaredField.setAccessible(true);
                Object o = declaredField.get(param[0]);
                preparedStatement.setObject(i + 1, o);

            }
        }

        //5 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();

        //6 返回封装的结果集
        ArrayList<Object> objects = new ArrayList<>();
        String resultType = mappedStatement.getResultType();
        Class<?> aClass1 = Class.forName(resultType);

        while (resultSet.next()) { //一个next 表示一行数据
            Object o = aClass1.newInstance();
            //获取所有字段名
            ResultSetMetaData metaData = resultSet.getMetaData();
            //查询出字段的数量
            int columnCount = metaData.getColumnCount();
            for (int i = 1; i <= columnCount; i++) {

                //字段名
                String columnName = metaData.getColumnName(i);
                //字段值
                Object value = resultSet.getObject(columnName);

                //使用反射或者自省完成对象的封装

//                //反射
                Field[] fields = aClass1.getDeclaredFields();
                for (Field field : fields) {
                    //属性名
                    String name = field.getName();
                    if (name.equals(columnName)) {
                        field.setAccessible(true);
                        field.set(o, value);
                    }
                }
                //自省
//                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName,aClass1);
//                Method writeMethod = propertyDescriptor.getWriteMethod();
//                writeMethod.invoke(o,value);

            }
            objects.add(o);

        }
        return (List<E>) objects;
    }

    /**
     * 作用 1 用？代替#{} ; 2 将#{} 里面的值取出来进行存储
     *
     * @param sql
     * @return
     */
    private BoundSql parseSql(String sql) {
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析后的sql
        String parse = genericTokenParser.parse(sql);
        //解析出来的参数字段
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        return new BoundSql(parse, parameterMappings);

    }
}
